#ifndef _CLOTH_H_
#define _CLOTH_H_

#include "argparser.h"
#include "boundingbox.h"
#define LB 0
#define RB 1
#define LT 2
#define RT 3

#define BOTTOM 11
#define TOP 12
#define RIGHT 13
#define LEFT 14
#define INNER 100

#define L_NEIGHBOR 0
#define R_NEIGHBOR 1
#define B_NEIGHBOR 2
#define T_NEIGHBOR 3
#define LB_NEIGHBOR 4
#define RB_NEIGHBOR 5
#define LT_NEIGHBOR 6
#define RT_NEIGHBOR 7
// =====================================================================================
// Cloth Particles
// =====================================================================================

class ClothParticle {
public:
  // ACCESSORS
  const Vec3f& getOriginalPosition() const{ return original_position; }
  const Vec3f& getPosition() const{ return position; }
  const Vec3f& getVelocity() const{ return velocity; }
  const Vec3f& getAcceleration() const { return acceleration; }
  Vec3f getForce() const { return mass*acceleration; }
  float getMass() const { return mass; }
  bool isFixed() const { return fixed; }
  int getNeighborNum(){ return neighborNum;}
  int getNeighborId(int index){return neighbors[index];}
  int getBoundaryPos(){ return boundaryPos;}
  Vec3f getForce(int neighborNum){return forces[neighborNum];}
  void printNeighbors(){for(int i=0;i<8;i++) cout<< neighbors[i]<< " ";
    cout<<endl;
    }


  // MODIFIERS
  void setOriginalPosition(const Vec3f &p) { original_position = p; }
  void setPosition(const Vec3f &p) { position = p; }
  void setVelocity(const Vec3f &v) { velocity = v; }
  void setAcceleration(const Vec3f &a) { acceleration = a; }
  void setMass(float m) { mass = m; }
  void setFixed(bool b) { fixed = b; }
  void setNeighborNum(int n) { neighborNum = n;}
 // void setNeighbors(int* n){ neighbors = n; cout << "###"<< n[0] << " " << n[1]<< " " << n[2]<< " " << n[3]<< endl;}
  void setBoundaryPos(int n){ boundaryPos = n;}
  void setNeighborId(int index,int id){ neighbors[index]=id;}
  void setForce(int neighborNum,Vec3f force){forces[neighborNum]=force;}
public:
  // REPRESENTATION
  Vec3f original_position;
  Vec3f position;
  Vec3f velocity;
  Vec3f acceleration;
  Vec3f forces[8];
  float mass;
  bool fixed;

  int neighbors[8];
  int neighborNum;
  int boundaryPos;



};

// =====================================================================================
// Cloth System
// =====================================================================================

class Cloth {

public:
  Cloth(ArgParser *args);
  ~Cloth() { delete [] particles; }

  // ACCESSORS
  const BoundingBox& getBoundingBox() const { return box; }

  // PAINTING & ANIMATING
  void Paint() const;
  void Animate();
  void computeDistancetoNeighbors(int id);


public:

  // PRIVATE ACCESSORS
  const ClothParticle& getParticle(int i, int j) const {
    assert (i >= 0 && i < nx && j >= 0 && j < ny);
    return particles[i + j*nx]; }
  ClothParticle& getParticle(int i, int j) {
    assert (i >= 0 && i < nx && j >= 0 && j < ny);
    return particles[i + j*nx]; }

  // HELPER FUNCTION
  void computeBoundingBox();

  // REPRESENTATION
  ArgParser *args;
  // grid data structure
  int nx, ny;
  ClothParticle *particles;
  BoundingBox box;
  // simulation parameters
  float damping;
  // spring constants
  float k_structural;
  float k_shear;
  float k_bend;
  // correction thresholds
  float provot_structural_correction;
  float provot_shear_correction;
};

#endif
